import * as THREE from '../../../../modules/three/three.module.js'
import {drawCircle, flatten} from "../util/StaticMethods.js";
import Cartesian3 from "../cesium/Core/Cartesian3.js";
import Ellipsoid from "../cesium/Core/Ellipsoid.js";
import {OrientationHelper} from "./OrientationHelper.js";

const tmpCartesian3 = new Cartesian3();
const tmp = new THREE.Vector3();
const tmp2 = new THREE.Vector3();
const tmp3 = new THREE.Vector3();
const tmp4 = new THREE.Vector3();
const tmp5 = new THREE.Vector3();
const line3 = new THREE.Line3();
const quat1 = new THREE.Quaternion();
const unitY = new THREE.Vector3(0, 1, 0);

const circleTexture = new THREE.Texture(drawCircle());
circleTexture.needsUpdate = true;
const color = '#fff';
const colorNoSpray = '#000';
const colorWalked = '#444';
const colorWalking = '#ff0000';
const colorWillWalk = '#0031b9';
const colorShortExpected = '#ffbc11';
const colorStart = '#ff6000';
const colorEnd = '#0f0';

const useCylinder = true;
const lineWidth = 0.03;
const emphasizeWidth = 0.035;
const arrowLength = 0.36;
const arrowRadius = 0.105;
const showHeadingIndicators = true

function createArrow(start, end, cssColorStr) {
  const geo = new THREE.CylinderGeometry(0, arrowRadius, arrowLength, 16);
  const mtl = new THREE.MeshBasicMaterial({wireframe: false, color: cssColorStr});
  const mesh = new THREE.Mesh(geo, mtl);
  const mat = new THREE.Matrix4().makeTranslation(0, -0.5 * arrowLength, 0);
  geo.applyMatrix4(mat);
  const orient = tmp4.copy(end).sub(start);
  const len = orient.length();
  const pos = tmp3.copy(start).lerp(end, arrowLength / len);
  mesh.position.copy(pos); // start
  orient.normalize();
  quat1.setFromUnitVectors(unitY, orient);
  mesh.quaternion.copy(quat1);
  return mesh;
}

function createCylinder(cssColorStr, radius = lineWidth) {
  const geo = new THREE.CylinderGeometry(radius, radius, 1, 4);
  const mtl = new THREE.MeshBasicMaterial({wireframe: false, color: cssColorStr});
  const mesh = new THREE.Mesh(geo, mtl);
  return mesh;
}

function transformCylinder(mesh, start, end) {
  const pos = tmp3.copy(start).lerp(end, 0.5);
  const orient = tmp4.copy(end).sub(start);
  const len = orient.length();
  orient.normalize();
  quat1.setFromUnitVectors(unitY, orient);
  mesh.position.copy(pos);
  mesh.quaternion.copy(quat1);
  mesh.scale.set(1, len, 1);
  return mesh;
}

// 负责渲染从 AI规划的 传给无人机飞控 的航线
// 目前只适用于 展示历史的无人机航线，支持实时的
export default class FlyPathIndicatorV2 extends THREE.Group {
  /**
   * 数据来自于 AI 的航线规划
   * @type {DroneFlyPath}
   */
  path
  /**
   * 负责绘制航点列表
   * @type {THREE.Points[]}
   */
  wayPoints = []
  /**
   * 起点 终点
   * @type {THREE.Points[]}
   */
  startEnd = []
  /**
   * 负责绘制航线的线段 列表
   * @type {THREE.LineSegments[] | THREE.Mesh[]}
   */
  segments = []
  /**
   * 负责绘制航线的线段 列表
   * @type {THREE.Mesh[]}
   */
  arrows = []
  /**
   * 无人飞到了当前航线的哪一段
   * @type {THREE.LineSegments | THREE.Mesh | null}
   */
  curSegment = null
  /**
   * 无人飞到了当前航线的哪一段
   * @type {THREE.Mesh | null}
   */
  curArrow = null
  /**
   * @type {THREE.LineSegments | THREE.Mesh}
   */
  segUnPass
  /**
   * 无人机真实坐标 和 在线段上的投影点之间的连线，越短越好
   * @type {THREE.LineSegments | THREE.Mesh}
   */
  segShortExpected

  /**
   * 地球直角坐标系坐标 乘以该矩阵 得到世界坐标系坐标
   * 直接指向 World3d._meta.toEarchCoordMatInv
   * @type {THREE.Matrix4}
   * @see {World3d._meta.toEarchCoordMatInv}
   */
  earthPosToWorld
  firstPosition = new THREE.Vector3()

  lineMtl = new THREE.LineBasicMaterial({
    color: color,
    toneMapped: false,
    transparent: true,
  })

  pointMtl = new THREE.PointsMaterial( {
    size: 12,
    color: color,
    sizeAttenuation: false,
    map: circleTexture,
    alphaTest: 0.5,
    toneMapped: false,
    transparent: true } )

  _finished = false;
  // true 表示避免红色线段和箭头回缩
  _avoidProgressGoBack = true;
  passingIndex = -1;
  redSegLen = 0;

  constructor(droneFlyPath, toEarchCoordMatInv) {
    super();

    this.path = droneFlyPath;
    this.earthPosToWorld = toEarchCoordMatInv;
    const segmentGeo = new THREE.BufferGeometry();
    segmentGeo.setAttribute('position', new THREE.BufferAttribute(flatten([tmp, tmp2]), 3));
    if (useCylinder) {
      this.segUnPass = createCylinder(colorWillWalk, emphasizeWidth);
    } else {
      this.segUnPass = new THREE.LineSegments(segmentGeo.clone(), this.lineMtl.clone());
      this.segUnPass.material.color.set(colorWillWalk);
    }
    this.add(this.segUnPass);
    this._drawPath();
    this.segShortExpected = new THREE.LineSegments(segmentGeo.clone(), this.lineMtl.clone());
    this.segShortExpected.material.color.set(colorShortExpected);
    this.segShortExpected.frustumCulled = false;
    this.segUnPass.frustumCulled = false;
    this.add(this.segShortExpected);
  }

  _drawPath() {
    const coords = this.path.droneCoords.map(it => {
      return new THREE.Vector3(it.x, it.y, it.z).applyMatrix4(this.earthPosToWorld);
    });
    if (coords.length) {
      this.firstPosition.copy(coords[0]);
    }
    let start, end;
    coords.forEach((vector3, index) => {
      const pointGeo = new THREE.BufferGeometry();
      pointGeo.setAttribute('position', new THREE.BufferAttribute(flatten([vector3]), 3));
      const wayPoint = new THREE.Points(pointGeo, this.pointMtl.clone());
      this.wayPoints.push(wayPoint);
      this.add(wayPoint);
      if (showHeadingIndicators) {
        const heading = this.path.facing[index]
        const headingIndicator = new OrientationHelper(false, heading, vector3)
        this.add(headingIndicator);
      }

      if (index > 0) {
        const pre = coords[index - 1];
        const cylinderColor = this.path.sprayFlags[index - 1] ? color : colorNoSpray;
        let segment;
        if (useCylinder) {
          segment = createCylinder(cylinderColor);
          transformCylinder(segment, pre, vector3);
        } else {
          const segmentGeo = new THREE.BufferGeometry();
          segmentGeo.setAttribute('position', new THREE.BufferAttribute(flatten([pre, vector3]), 3));
          segment = new THREE.LineSegments(segmentGeo, this.lineMtl.clone());
          segment.renderOrder = 2;
        }
        segment.userData = [pre, vector3];
        this.segments.push(segment);
        this.add(segment);

        const arrowHelper = createArrow(pre, vector3, cylinderColor);
        this.add(arrowHelper);
        this.arrows.push(arrowHelper);
      }
      if (index === 0) {
        start = new THREE.Points(pointGeo, this.pointMtl.clone());
        start.material.size = 18;
        start.material.color.setStyle(colorStart);
        this.add(start);
      } else if (index === coords.length - 1) {
        end = new THREE.Points(pointGeo, this.pointMtl.clone());
        end.material.size = 18;
        end.material.color.setStyle(colorEnd);
        this.add(end);
      }
    });
    this.startEnd = [start, end]
  }

  /**
   * 更新航点颜色
   * @param {number} targetIndex0表示已经飞过的航点序号，航点从0开始计数
   */
  updateFlyProgress(targetIndex0) {
    // this.curSegment = null;
    const targetIndex = targetIndex0 + 1;
    if (this._finished || targetIndex < 1 || targetIndex > this.wayPoints.length) {
      return;
    }
    const colorWait = this.path.sprayFlags[targetIndex0] ? colorWillWalk : colorNoSpray;
    this.segUnPass.material.color.setStyle(colorWait);
    let passingIndexChanged = this.curSegment === null;
    if (this.passingIndex > -1 && this.passingIndex !== targetIndex0) {
      this.redSegLen = 0;
      passingIndexChanged = true;
    }
    this.passingIndex = targetIndex0;

    const preIndex = targetIndex0;
    this.wayPoints.forEach((it, index) => {
      if (index <= preIndex) {
        it.material.color.setStyle(colorWalked);
      } else if (index === targetIndex) {
        it.material.color.setStyle(colorWalking);
      } else {
        it.material.color.setStyle(colorWillWalk);
      }
    });
    this.segments.forEach((it, index) => {
      const arrowMesh = this.arrows[index];
      if (index < preIndex) {
        it.material.color.setStyle(colorWalked);
        arrowMesh.material.color.setStyle(colorWalked);
        arrowMesh.position.copy(it.userData[1]);
      } else if (index === preIndex) {
        if (this.curSegment && this.curSegment !== it) {
          this.curSegment.material.color.setStyle(colorWalked);
          const [start, end] = this.curSegment.userData;
          if (useCylinder) {
            // 刚刚走完的路径线段，重置其长度
            transformCylinder(this.curSegment, start, end);
          } else {
            const posAttr = this.curSegment.geometry.attributes.position;
            posAttr.setXYZ(1, end.x, end.y, end.z);
            posAttr.needsUpdate = true;
          }
        }
        this.curSegment = it;
        this.curArrow = arrowMesh;
        this.curSegment.material.color.setStyle(colorWalking);
        this.curArrow.material.color.setStyle(colorWalking);
        if (passingIndexChanged && useCylinder) {
          // 使蓝色圆柱体完成遮挡 红色的圆柱
          transformCylinder(this.segUnPass, it.userData[0], it.userData[1]);
        }
      } else {
        const colorWait = this.path.sprayFlags[index] ? colorWillWalk : colorNoSpray;
        it.material.color.setStyle(colorWait);
        arrowMesh.material.color.setStyle(colorWait);
      }
    });
    if (targetIndex === this.wayPoints.length) {
      this._finished = true;
      this.curSegment = null;
      this.segUnPass.visible = false;
    }
  }

  /**
   * 根据无人机实时位置，更细致的展示航线进度
   */
  updateByDroneRealPosition(lng, lat, altitude) {
    let curSegment = this.curSegment;
    // if (!this.curSegment) {
    //   curSegment = this.segments[0];
    // }
    if (!curSegment) {
      return;
    }
    const curArrow = this.curArrow;
    const [start, end] = curSegment.userData;
    line3.start.copy(start);
    line3.end.copy(end);
    const earthCoord = Cartesian3.fromDegrees(lng, lat, altitude, Ellipsoid.WGS84, tmpCartesian3);
    tmp.set(earthCoord.x, earthCoord.y, earthCoord.z).applyMatrix4(this.earthPosToWorld);
    const ptOnLine = line3.closestPointToPoint(tmp, true, tmp2);
    const {x, y, z} = ptOnLine
    if (useCylinder) {
      const len = start.distanceTo(ptOnLine);
      if (!this._avoidProgressGoBack || len > this.redSegLen) {
        if (len > arrowLength) {
          // 使红色圆柱体和箭头圆锥体不重合
          tmp5.copy(ptOnLine).lerp(start, arrowLength / len);
          transformCylinder(curSegment, start, tmp5);
        } else {
          transformCylinder(curSegment, start, ptOnLine);
        }
        transformCylinder(this.segUnPass, ptOnLine, end);
        curArrow.position.copy(ptOnLine);
        this.redSegLen = len;
      }
    } else {
      const posAttr1 = curSegment.geometry.attributes.position;
      const posAttr2 = this.segUnPass.geometry.attributes.position;
      posAttr1.setXYZ(0, line3.start.x, line3.start.y, line3.start.z);
      posAttr1.setXYZ(1, x, y, z);
      posAttr2.setXYZ(0, x, y, z);
      posAttr2.setXYZ(1, line3.end.x, line3.end.y, line3.end.z);
      posAttr1.needsUpdate = true;
      posAttr2.needsUpdate = true;
      curArrow.position.copy(ptOnLine);
    }
    const posAttr3 = this.segShortExpected.geometry.attributes.position;
    posAttr3.setXYZ(0, x, y, z);
    posAttr3.setXYZ(1, tmp.x, tmp.y, tmp.z);
    posAttr3.needsUpdate = true;
  }
}
